package com.tbit.common.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

import static com.tbit.common.constant.RpcConstants.*;


/**
 * Rpc调用鉴权
 * 因为我们的服务是nginx直接指向的，没有经过统一的网关进行流量分发
 * 所以为了避免外部http直接接口调用，要对内部的rpc调用也进行鉴权
 * 鉴权方式为: 根据特定请求头标识的内部调用签名校验 进行鉴权
 * @author <a href="mailto:chen.wutian@tbit.com.cn">BUGTian</a>
 * @since 2024/9/14
 */
@Slf4j
public class RpcSecurityUtil {

    /**
     * 加密串的盐
     */
    private static final String SALT = "tbit-rpc";
    /**
     * 默认10秒有效期
     */
    private static final long VALIDITY_PERIOD = 10 * 1000;

    /**
     * 生成签名
     * @param time 当前时间戳
     * @return 签名
     */
    public static String encryption(Long time) {
        return SecureUtil.md5(SALT + time);
    }

    /**
     * 校验签名是否有效
     * @param request 请求
     * @return true：通过   false：不通过
     */

    public static boolean authentication(HttpServletRequest request) {
        // 只过滤 API 请求的地址
        if (StrUtil.startWith(request.getRequestURI(), RPC_API_PREFIX)) {
            String time = request.getHeader(RPC_HEADER_TIME);
            String secret = request.getHeader(RPC_HEADER_SECRET);
            if (StringUtils.hasText(time) && StringUtils.hasText(secret)) {
                //判断请求时间是否超期
                if (Math.abs(System.currentTimeMillis() - Long.parseLong(time)) < VALIDITY_PERIOD) {
                    //校验签名
                    if (encryption(Long.parseLong(time)).equals(secret)) {
                        return true;
                    }
                }
            }
            log.warn("Rpc调用鉴权不通过，请求接口：{}，time：{}，secret：{}", request.getRequestURI(), time, secret);
        }
        return false;
    }
}
